iT邦幫忙

2022 iThome 鐵人賽

DAY 20
0
自我挑戰組

SPYxFRONTEND ~ 懂一點後端真是讓人哇哭哇哭系列 第 20

[Day20] 用 React Router + useForm + axios.post 打造一個簡易後台

  • 分享至 

  • xImage
  •  

前言

一樣開始前請記得在 local 端開好前端、後段:
後端:json-server --watch db.json --port 10000 --routes routes.json
前端:npm start

本日正文

這幾天的範例都圍繞在餐廳,
我們現在也知道 axios 的 CRUD 該怎麼做了,
所以是時候來做一個簡易後台了吧?
(就是讓使用者可以在表單自行輸入後,前台就會出現新增的資料)

先初步想一下我們可能會需要哪些元件跟功能:

  1. 表單(form)
  2. 新增資料(axios.post)
  3. 切換前後台

用 React Router 把頁面架構拉出來

好,那我們大概可以下手了,
首先先從"切換前後台"開始,還記得昨天介紹的 React Router 嗎?
那我們先把主要的架構寫出來,
像這樣:

<Flex justifyContent="center">
      <Router>
        <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/admin" element={<AdminPage />} />
        </Routes>
    </Router>
  </Flex>

這邊簡單說明一下,這邊設定了兩條路徑,
一個在 /,另一個在 admin
也就是首頁 <Home /> 跟 後台頁面 <AdminPage />
因此我們在 element 各別這樣設定之後,
我們就要來宣告首頁要顯示什麼,後台頁面要顯示什麼。

const Home = () => {
  return (
    <div>
      <Button m={2} onClick={fetchData}>全部資料</Button>
      <Button m={2} onClick={searchData} colorScheme="teal">人氣料理</Button>
      <Link to={`/admin`}>
        <Button m={2} colorScheme="twitter">後台管理</Button>
      </Link>
      <OrderedList px={4}>
          {sales?.map((x) => (
            <ListItem my={2}>
              <Flex>
                <Text>{x.item}</Text>
                {x.count >= hitCount ? (
                  <Tag ml={2} size="sm" variant="solid" colorScheme="red">
                    人氣料理
                  </Tag>
                ) : null}
              </Flex>
              <Text>銷售量:{x.count}</Text>
            </ListItem>
          ))}
        </OrderedList>
    </div>
  )
}

這邊幾乎就是把之前寫的 code 搬進去 <Home> 裡面,
只是今天我們多了後台管理頁面,
所以加了一個會導向 /admin 的按鈕,
像這樣:

<Link to={`/admin`}>
    <Button m={2} colorScheme="twitter">後台管理</Button>
  </Link>

再來就是我們要設定後台管理頁面要顯示什麼,
這邊先不把表單寫進來,
我們先測試前後台切換O不OK就好,
因此後台也要新增一個回前台的按鈕,
像這樣:

const AdminPage = () => {
  return (
      <div>
        <Link to={`/`}>
          <Button colorScheme="blue">回前台</Button>
        </Link>
        <Text my={4} as="h2" fontSize="2xl" textAlign="center">
          {today.getFullYear()} 鐵人餐廳 - 後台管理
        </Text>
      </div>
  )
}

那我們先來看一下效果:

看起來前後台頁面切換沒問題了,
那我們就來搞定後台的表單吧。

用 useForm 讓你輕鬆的控制表單欄位

還記得之前我們在做搜尋功能時,
我們是用 <Input> 的元件,
然後再設定 onChange 時把文字框輸入的值用 useState 存下來,
像這樣:

<Input placeholder='請輸入要搜尋的關鍵字...' size='md' onChange={(e) => setSearchKeywords(e.target.value)} />

但這樣不就代表我有幾個欄位就要宣告幾個 useState 設定 onChange 幾次?
這樣會不會太麻煩了?

因此這邊就要介紹 react-hook-formuseForm hook,
它可以讓你直接在表單上註冊該欄位,
然後就可以直接取得值,
那我們直接試試看吧!

首先一樣先安裝 react-hook-form 跟 import useForm
像這樣:
https://ithelp.ithome.com.tw/upload/images/20220921/20129873iKTeAPvmN1.png

import { useForm } from "react-hook-form";

再來我們要在主程式的地方宣告這些:

export default function Home() {
  const { register, handleSubmit, reset } = useForm();
  const submit = data => console.log(data);
  ... (略)

再來就是回到後台頁面的地方,
我們要把 form 表單給放進去,
像這樣:

const AdminPage = () => {
  return (
      <div>
        <Link to={`/`}>
          <Button colorScheme="blue">回前台</Button>
        </Link>
        <Text my={4} as="h2" fontSize="2xl" textAlign="center">
          {today.getFullYear()} 鐵人餐廳 - 後台管理
        </Text>
        <form onSubmit={handleSubmit(submit)}>
          <FormLabel my={4}>品項</FormLabel>
          <Input placeholder='請輸入品項' type="text" {...register("item", { required: true })} />
          <FormLabel my={4}>數量</FormLabel>
          <Input placeholder='請輸入數量' type="number" {...register("count", { required: true })} />
          <Button my={4} type="submit">送出</Button>
        </form>
      </div>
  )
}

先看一下現在介面是什麼樣子:
https://ithelp.ithome.com.tw/upload/images/20220921/20129873KyXAlfXWm3.png

register:註冊欄位,handleSubmit:處理表單送出

這邊解釋一下關鍵的地方,
第一個 <Input> 裡有寫 {...register("item")} 這是代表這個文字框被註冊成 item 這個欄位,
以此類推,第二的 <Input> 裡有寫 {...register("count")} 就是這個文字框被註冊成 count 的欄位。

回到 <form> 的設定,
<form onSubmit={handleSubmit(submit)}> 的意思就是說當表單被送出時,會去執行 handleSubmit(submit)
submit 這個 function 就是稍早我們有設定的 const submit = data => console.log(data); 地方,
那我們來試一下我們現在輸入表單並送出會發生什麼事吧!

https://ithelp.ithome.com.tw/upload/images/20220921/201298736Fn7s0CPV9.png

神奇的事發生了,
它會自動把剛剛輸入的內容包裝成 JSON 格式的樣子並回傳:

{
    "item": "ABC",
    "count": "123"
}

直接拿 useForm 回傳的 data 搭配 axios.post

所以拿到資料之後,
我們就可以很順利的 call axios.post 了對吧,
那當然我們就是要寫在 submit 的 function 之中,
像這樣:

const submit = data => {
    console.log(data);
    axios.post(`${hostUrl}/api/v1/sales`, data)
    .then(res => {
      console.log(res.data);
    });
  }

這邊你可以發現 data 可以直接代入到 axios.post 第二個參數之中,
不需要再經過處理。

那我們整個來試試看效果如何吧!

看起來我們成功把前後台串起來了!!!!!
然後還有個小小的地方,
就是你可能發現表單送出後不會清空,
這邊 useForm 也有提供 reset (清空表單) 的功能,
那我們把它加入之後再整個試一次吧!

const submit = data => {
    console.log(data);
    axios.post(`${hostUrl}/api/v1/sales`, data)
    .then(res => {
      console.log(res.data);
      reset();
    });
  }

看起來這個簡易後台都OK了!
總結一下,今天我們用了 React Router 串前後台頁面,
再用 useForm 搭配 axios.post 寫出簡易後台進行資料的新增。

用今天這篇文章為這個階段做個小小收尾,
這個階段我們知道如何去 call API 進行資料的 CRUD。
那接下來就要進入下一階段了,
明天的文章再見囉!

後記

終於來到第20天,
最後10天,加油~~~

https://ithelp.ithome.com.tw/upload/images/20220921/20129873zqyX62Mjcc.png


上一篇
[Day19] 如何用 React Router 在網址列加上 ?q= 的參數
下一篇
[Day21] 遇到 BUG 時,前端和後端互相甩鍋?
系列文
SPYxFRONTEND ~ 懂一點後端真是讓人哇哭哇哭30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言